package com.atguigu.tingshu.album.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.atguigu.tingshu.album.config.VodConstantProperties;
import com.atguigu.tingshu.album.mapper.AlbumInfoMapper;
import com.atguigu.tingshu.album.mapper.AlbumStatMapper;
import com.atguigu.tingshu.album.mapper.TrackInfoMapper;
import com.atguigu.tingshu.album.mapper.TrackStatMapper;
import com.atguigu.tingshu.album.service.TrackInfoService;
import com.atguigu.tingshu.album.service.VodService;
import com.atguigu.tingshu.common.cache.CacheLock;
import com.atguigu.tingshu.common.cache.GuiGuCache;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.util.UploadFileUtil;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.album.TrackStat;
import com.atguigu.tingshu.query.album.TrackInfoQuery;
import com.atguigu.tingshu.user.client.UserFeignClient;
import com.atguigu.tingshu.vo.album.*;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qcloud.vod.VodUploadClient;
import com.qcloud.vod.model.VodUploadRequest;
import com.qcloud.vod.model.VodUploadResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.annotations.Param;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.multipart.MultipartFile;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class TrackInfoServiceImpl extends ServiceImpl<TrackInfoMapper, TrackInfo> implements TrackInfoService {

    @Autowired
    private TrackInfoMapper trackInfoMapper;
    @Autowired
    private TrackStatMapper trackStatMapper;
    @Autowired
    private AlbumInfoMapper albumInfoMapper;
    @Autowired
    private VodConstantProperties properties;
    @Autowired
    private VodService vodService;
    @Autowired
    private UserFeignClient userFeignClient;
    @Autowired
    private AlbumStatMapper albumStatMapper;

    /**
     * 上传声音
     *
     * @param file
     * @return
     */
    @Override
    public Map<String, String> uploadTrack(MultipartFile file) {
        try {
            //0.微信端上传文件转存到本地暂存区
            String path = UploadFileUtil.uploadTempPath(properties.getTempPath(), file);
            //1.初始化一个上传客户端对象
            VodUploadClient client = new VodUploadClient(properties.getSecretId(), properties.getSecretKey());
            //2.构造上传请求对象
            VodUploadRequest request = new VodUploadRequest();
            request.setMediaFilePath(path);
            //3.调用上传
            VodUploadResponse response = client.upload(properties.getRegion(), request);
            //4.封装返回结果
            if (response != null) {
                Map<String, String> map = new HashMap<>();
                map.put("mediaFileId", response.getFileId());
                map.put("mediaUrl", response.getMediaUrl());
                return map;
            }
            return null;
        } catch (Exception e) {
            log.error("[云点播上传文件失败：]{}", e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 保存声音信息
     *
     * @param trackInfoVo
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveTrackInfo(Long userId, @Validated TrackInfoVo trackInfoVo) {
        //1.将VO转为实体类
        TrackInfo trackInfo = BeanUtil.copyProperties(trackInfoVo, TrackInfo.class);
        trackInfo.setUserId(userId);
        //声音状态默认设置为通过，正常应该有审核是否通过
        trackInfo.setStatus(SystemConstant.TRACK_STATUS_PASS);
        //声音来源默认为用户原创
        trackInfo.setSource(SystemConstant.TRACK_SOURCE_USER);
        //1.5 设置声音序号 根据专辑ID查询专辑得到该专辑下已有声音数量
        AlbumInfo albumInfo = albumInfoMapper.selectById(trackInfo.getAlbumId());
        trackInfo.setOrderNum(albumInfo.getIncludeTrackCount() + 1);

        //2.  查询声音文件信息(音频大小、类型、时长)
        TrackMediaInfoVo trackMediaInfoVo = vodService.DescribeMediaInfos(trackInfo.getMediaFileId());
        //3.保存信息
        if (trackMediaInfoVo != null) {
            trackInfo.setMediaSize(trackMediaInfoVo.getSize());
            trackInfo.setMediaType(trackMediaInfoVo.getType());
            trackInfo.setMediaUrl(trackMediaInfoVo.getMediaUrl());
            trackInfo.setMediaDuration(BigDecimal.valueOf(trackMediaInfoVo.getDuration()));
        }
        trackInfoMapper.insert(trackInfo);
        //4.更新专辑（专辑包含声音数量）
        albumInfo.setIncludeTrackCount(albumInfo.getIncludeTrackCount() + 1);
        albumInfoMapper.updateById(albumInfo);
        //5.保存声音统计信息
        saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_PLAY);
        saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_COLLECT);
        saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_PRAISE);
        saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_COMMENT);
    }

    public void saveTrackStat(Long trackId, String statType) {
        TrackStat trackStat = new TrackStat();
        trackStat.setTrackId(trackId);
        trackStat.setStatType(statType);
        trackStat.setStatNum(0);
        trackStatMapper.insert(trackStat);
    }

    /**
     * 获取当前用户声音分页列表
     *
     * @param listVoPage
     * @param trackInfoQuery
     * @return
     */
    @CacheLock(
            cachePrefix = "userTrack:",
            lockPrefix = "lock:userTrack:",
            cacheExpire = 1 * 60 * 60 * 24,
            key = "#trackInfoQuery.userId + ':' + #listVoPage.current + ':' + #trackInfoQuery.status"
    )
    public Page<TrackListVo> findUserTrackPage(Page<TrackListVo> listVoPage, TrackInfoQuery trackInfoQuery) {
        Page<TrackListVo> page = trackInfoMapper.findUserTrackPage(listVoPage, trackInfoQuery);
        return page;
    }

    /**
     * 修改声音信息
     *
     * @param id          声音ID
     * @param trackInfoVo 变更后声音信息vo
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateTrackInfo(Long id, TrackInfoVo trackInfoVo) {
        //1.先获取声音信息-得到更新前声音文件唯一标识
        TrackInfo trackInfo = trackInfoMapper.selectById(id);
        String mediaFileId = trackInfo.getMediaFileId();

        BeanUtil.copyProperties(trackInfoVo, trackInfo);

        //2.调用腾讯云点播获取声音媒体信息（时长，类型，文件大小） 更新媒体信息
        if (!mediaFileId.equals(trackInfo.getMediaFileId())) {
            TrackMediaInfoVo trackMediaInfo = vodService.DescribeMediaInfos(trackInfo.getMediaFileId());
            trackInfo.setMediaType(trackMediaInfo.getType());
            trackInfo.setMediaSize(trackMediaInfo.getSize());
            trackInfo.setMediaDuration(BigDecimal.valueOf(trackMediaInfo.getDuration()));
        }

        //3.更新声音信息表
        trackInfoMapper.updateById(trackInfo);
    }

    /**
     * 删除声音信息
     *
     * @param id
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void removeTrackInfo(Long id) {
        //1.根据id查询声音
        TrackInfo trackInfo = trackInfoMapper.selectById(id);
        //2.order_num重新排序
        trackInfoMapper.updateOrderNum(trackInfo.getAlbumId(), trackInfo.getOrderNum());
        //3.删除声音信息
        trackInfoMapper.deleteById(id);
        //4.删除声音统计信息
        trackStatMapper.delete(new LambdaQueryWrapper<TrackStat>().eq(TrackStat::getTrackId, id));
        //5.修改专辑信息中声音数量
        AlbumInfo albumInfo = albumInfoMapper.selectById(trackInfo.getAlbumId());
        albumInfo.setIncludeTrackCount(albumInfo.getIncludeTrackCount() - 1);
        albumInfoMapper.updateById(albumInfo);
        //6.删除云点播声音文件
        vodService.deleteMedia(trackInfo.getMediaFileId());
        //7. 删除声音封面
    }

    @Override
    public Page<AlbumTrackListVo> getUserAlbumTrackPage(Page<AlbumTrackListVo> pageInfo, Long userId, Long albumId) {
        //1.根据专辑ID分页查询声音列表-（包含声音统计信息）
        pageInfo = trackInfoMapper.getUserAlbumTrackPage(pageInfo, albumId);
        List<AlbumTrackListVo> trackList = pageInfo.getRecords();
        // TODO 当前页声音付费标识业务处理 关键：找出声音需要付费情况处理
        //2.根据专辑ID查询专辑信息-得到专辑付费类型以及免费试听集数（VIP免费或者付费才有）
        AlbumInfo albumInfo = albumInfoMapper.getAlbumInfo(albumId);
        //2.1获取专辑付费类型 0101-免费、0102-vip免费、0103-付费
        String payType = albumInfo.getPayType();
        //3.用户未登录
        if (userId == null) {
            //3.1 如果不是免费类型
            if (!payType.equals(SystemConstant.ALBUM_PAY_TYPE_FREE)) {
                //方式一
                trackList.stream().map(track -> {
                    if (track.getOrderNum() > albumInfo.getTracksForFree()) {
                        track.setIsShowPaidMark(true);
                    }
                    return track;
                }).collect(Collectors.toList());
                //方式二
//                 trackList.stream().filter(trackInfo -> {
//                     return trackInfo.getOrderNum() > albumInfo.getTracksForFree();
//                 }).collect(Collectors.toList()).stream().forEach(trackInfo -> {
//                     trackInfo.setIsShowPaidMark(true);
//                 });

            }
        } else {
            //4.用户已登录  如果登录为普通用户或VIP过期了->未购买专辑未购买相关声音，将声音设置为付费标识；付费的专辑->未购买相关专辑或相关声音，将声音设置为付费
            //4.1 远程调用用户微服务获取用户信息（VIP状态）
            UserInfoVo userInfoVo = userFeignClient.getUserInfoVoByUserId(userId).getData();
            //声明变量是否需要购买
            boolean isNeedPay = false;
            //4.2 付费类型：VIP免费-->普通用户或者VIP过期 进一步查看用户是否购买过专辑或者声音
            if (SystemConstant.ALBUM_PAY_TYPE_VIPFREE.equals(payType)) {
                if (userInfoVo.getIsVip().intValue() == 0) {
                    //普通用户
                    isNeedPay = true;
                }
                if (userInfoVo.getIsVip().intValue() == 1 && userInfoVo.getVipExpireTime().before(new Date())) {
                    //VIP会员 会员过期- 会员到期时间 在 当前时间之前  -- 后期会有延迟任务更新会员过期时间
                    isNeedPay = true;
                }
            } else if (SystemConstant.ALBUM_PAY_TYPE_REQUIRE.equals(payType)) {
                //4.3 付费类型：付费-->通用户或者VIP,进一步查看用户是否购买过专辑或者声音
                isNeedPay = true;
            }


            //5.统一处理需要购买情况：如果用户未购买专辑或者声音，将声音付费标识改为：true
            if (isNeedPay) {
                //5.1 得到当前页中声音列表--需要将免费试听集数过滤掉
                List<AlbumTrackListVo> trackListVoList = trackList.stream().filter(trackInfo -> {
                    //将试听的集数过滤掉
                    return trackInfo.getOrderNum() > albumInfo.getTracksForFree();
                }).collect(Collectors.toList());

                //判断将免费试听集数过滤掉该声音列表是否为空
                if (trackListVoList.size() > 0) {
                    //需要进一步验证用户购买情况声音ID
                    List<Long> trackIdList = trackListVoList.stream().map(AlbumTrackListVo::getTrackId).collect(Collectors.toList());

                    //5.2 远程调用用户微服务-查询当前页中声音列表购买情况Map<声音ID，购买状态>
                    Map<Long, Integer> buyStatusMap = userFeignClient.userIsPaidTrackList(userId, albumId, trackIdList).getData();

                    //5.3 如果当前页中声音未购买 将指定声音付费标识改为：true
                    for (AlbumTrackListVo albumTrackListVo : trackListVoList) {
                        //获取声音购买结果
                        Integer isBuy = buyStatusMap.get(albumTrackListVo.getTrackId());
                        if (isBuy == 0) {
                            //说明当前用户未购买该专辑或者该声音
                            albumTrackListVo.setIsShowPaidMark(true);
                        }
                    }
                }
            }
        }

        //4.用户登录
        return pageInfo;
    }

    /**
     * 更新声音统计信息
     *
     * @param albumId  专辑ID
     * @param trackId  声音ID
     * @param statType 统计类型
     * @param count    数量
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateStat(Long albumId, Long trackId, String statType, Integer count) {
        //1.更新声音统计信息
        trackStatMapper.updateStat(trackId, statType, count);

        //2.更新专辑统计信息（如果是声音播放量或者声音评论量同步修改专辑统计信息）
        if (SystemConstant.TRACK_STAT_PLAY.equals(statType)) {
            //TODO 主要要将播放的统计类型 改为 声音的统计类型 “0401”
            albumStatMapper.updateStat(albumId, SystemConstant.ALBUM_STAT_PLAY, count);
        }
        if (SystemConstant.TRACK_STAT_COMMENT.equals(statType)) {
            //TODO 主要要将播放的统计类型 改为 声音的统计类型 “0404”
            albumStatMapper.updateStat(albumId, SystemConstant.ALBUM_STAT_COMMENT, count);
        }
    }

    /**
     * 根据声音ID，查询声音统计信息
     *
     * @param trackId
     * @return
     */
    @Override
    public TrackStatVo getTrackStatVo(Long trackId) {
        return trackInfoMapper.getTrackStatVo(trackId);
    }

    /**
     * 查询当前用户分集购买声音列表
     *
     * @param userId
     * @param trackId
     * @return 分集对象包含（名称，价格，数量）  用户可以购买声音列表（除了已购买声音外）
     */
    @Override
    public List<Map<String, Object>> getUserTrackWaitPayList(Long userId, Long trackId) {
        //1.根据声音ID查询声音对象（用于获取专辑ID）
        TrackInfo trackInfo = trackInfoMapper.selectById(trackId);
        Assert.notNull(trackInfo, "声音不存在！");

        //2.根据专辑ID+当前选中声音序号（序号大于等于当前声音序号声音列表），按照序号正序排-得到“待购”声音集合
        LambdaQueryWrapper<TrackInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(TrackInfo::getAlbumId, trackInfo.getAlbumId());
        queryWrapper.ge(TrackInfo::getOrderNum, trackInfo.getOrderNum());
        queryWrapper.orderByAsc(TrackInfo::getOrderNum);
        List<TrackInfo> waitBuyTrackInfoList = trackInfoMapper.selectList(queryWrapper);

        //3.远程调用“用户服务”根据专辑ID获取用户已购声音ID列表
        List<Long> userPaidTrckIdList = userFeignClient.getUserPaidTrackList(trackInfo.getAlbumId()).getData();

        //4.将已购声音ID排除到分集购买以外
        if (CollectionUtil.isNotEmpty(userPaidTrckIdList)) {
            //用户已买过当前专辑下声音，过滤条件：待购声音对象ID没有出现在已购声音ID集合中得到未买声音
            waitBuyTrackInfoList = waitBuyTrackInfoList.stream()
                    .filter(trackInfo1 -> !userPaidTrckIdList.contains(trackInfo1.getId())).collect(Collectors.toList());
        }

        //5.对预购的声音ID集合处理，动态展示“分集购买对象” 例如：本集、 后10集、后18集
        List<Map<String, Object>> list = new ArrayList<>();
        //获取待购声音数量
        int count = waitBuyTrackInfoList.size();
        // 获取声音单价
        AlbumInfo albumInfo = albumInfoMapper.selectById(trackInfo.getAlbumId());
        BigDecimal price = albumInfo.getPrice();
        //5.1 分集购买对象-本集 必然显示
        Map<String, Object> currMap = new HashMap<>();
        currMap.put("name", "本集");
        currMap.put("price", price);
        currMap.put("trackCount", 1);
        list.add(currMap);

        //5.2 待购声音数量<=10 则动态显示 后*集  采用循环优化
        //if (count <= 10) {
        //    Map<String, Object> map = new HashMap<>();
        //    map.put("name", "后" + count + "集");
        //    map.put("price", price.multiply(BigDecimal.valueOf(count)));
        //    map.put("trackCount", count);
        //    list.add(map);
        //}
        ////5.3 待购声音数量>10 则固定显示 后10集
        //if (count > 10) {
        //    Map<String, Object> map = new HashMap<>();
        //    map.put("name", "后10集");
        //    map.put("price", price.multiply(BigDecimal.valueOf(10)));
        //    map.put("trackCount", 10);
        //    list.add(map);
        //}
        //
        ////5.3 待购声音数量>10 且 <=20 则动态显示 后*集
        //if (count > 10 && count <= 20) {
        //    Map<String, Object> map = new HashMap<>();
        //    map.put("name", "后" + count + "集");
        //    map.put("price", price.multiply(BigDecimal.valueOf(count)));
        //    map.put("trackCount", count);
        //    list.add(map);
        //}
        ////5.4 待购声音数量>20 则固定显示 后20集
        //if (count > 20) {
        //    Map<String, Object> map = new HashMap<>();
        //    map.put("name", "后20集");
        //    map.put("price", price.multiply(BigDecimal.valueOf(20)));
        //    map.put("trackCount", 20);
        //    list.add(map);
        //}
        //
        ////5.5 待购声音数量>20 且 <=30 则动态显示 后*集
        //if (count > 20 && count <= 30) {
        //    Map<String, Object> map = new HashMap<>();
        //    map.put("name", "后" + count + "集");
        //    map.put("price", price.multiply(BigDecimal.valueOf(count)));
        //    map.put("trackCount", count);
        //    list.add(map);
        //}
        //if (count > 30) {
        //    Map<String, Object> map = new HashMap<>();
        //    map.put("name", "后" + count + "集");
        //    map.put("price", price.multiply(BigDecimal.valueOf(count)));
        //    map.put("trackCount", count);
        //    list.add(map);
        //}

        for (int i = 10; i <= 50; i += 10) {
            //判断 待购声音数量>10、20、30、40、50 则固定显示后 i 集
            if (count > i) {
                BigDecimal totalPrice = price.multiply(new BigDecimal(i));
                Map<String, Object> map = new HashMap<>();
                map.put("name", "后" + i + "集");
                map.put("price", totalPrice);
                map.put("trackCount", i);
                list.add(map);
            } else {
                //待购声音数量在区间内 需要动态显示集(剩余所有的分集)
                BigDecimal totalPrice = price.multiply(new BigDecimal(count));
                Map<String, Object> map = new HashMap<>();
                map.put("name", "后" + count + "集");
                map.put("price", totalPrice);
                map.put("trackCount", count);
                list.add(map);
                break;
            }
        }
        return list;
    }

    /**
     * 查询当前用户待购声音列表-用于渲染声音结算页面
     *
     * @param userId     用户ID
     * @param trackId    选择声音ID
     * @param trackCount 包含当前选择声音后续声音数量
     * @return
     */
    @Override
    public List<TrackInfo> getWaitPayTrackInfoList(Long userId, Long trackId, Integer trackCount) {
        //1.根据声音ID查询声音对象（用于获取专辑ID）
        TrackInfo trackInfo = trackInfoMapper.selectById(trackId);
        Assert.notNull(trackInfo, "声音不存在！");

        //2.根据专辑ID+当前选中声音序号（序号大于等于当前声音序号声音列表），按照序号正序排-得到“待购”声音集合  limit count 有可能包含已购声音
        LambdaQueryWrapper<TrackInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(TrackInfo::getAlbumId, trackInfo.getAlbumId());
        queryWrapper.ge(TrackInfo::getOrderNum, trackInfo.getOrderNum());
        queryWrapper.orderByAsc(TrackInfo::getOrderNum);
        queryWrapper.last("limit " + trackCount);
        List<TrackInfo> waitBuyTrackInfoList = trackInfoMapper.selectList(queryWrapper);

        //3.远程调用“用户服务”根据专辑ID获取用户已购声音ID列表
        List<Long> userPaidTrckIdList = userFeignClient.getUserPaidTrackList(trackInfo.getAlbumId()).getData();

        //4.将已购声音ID排除到分集购买以外
        if (CollectionUtil.isNotEmpty(userPaidTrckIdList)) {
            //用户已买过当前专辑下声音，过滤条件：待购声音对象ID没有出现在已购声音ID集合中得到未买声音
            waitBuyTrackInfoList = waitBuyTrackInfoList.stream()
                    .filter(trackInfo1 -> !userPaidTrckIdList.contains(trackInfo1.getId())).collect(Collectors.toList());
        }
        return waitBuyTrackInfoList;
    }
}
